参数
当函数接受参数时,就必须指定以下内容:
- 函数在其定义中指定接受的参数列表,以及这些参数的类型。
- 在每个函数调用中提供匹配的实参列表。
仔细阅读 C#规范会发现行參( parameter )和实参( argument )之间存在一些细微的区别:行參是函数定义的一部分,而实参则由调用代码传递给函数。但是,这两个术语通常被简单地称为参数,似乎没有人对此感到十分不满。
示例代码如下所示,其中可以有任意数量的参数,每个参数都有一个类型和一个名称:
static <returnType> <FunctionName>(<paramType> <paramName>, ... )
{
...
return <returnValue>;
}
参数用逗号分隔开。每个参数都在函数的代码中用作一个变量。例如,下面是一个简单的函数,带有两个 double
参数,并返回它们的乘积:
static double Product (double param1, double param2)
{
return param1 * parma2;
}
下面看一个较复杂的示例。
把下列代码添加到
Program.cs
中:
class Program { static int MaxValue(int[] intArray) { int maxVal = intArray[0]; for (int i = 1; i < intArray.Length; i++) { if(intArray[i] > maxVal) maxVal = intArray[i]; } return maxVal; } static void Main(string[] args) { int[] myArray = { 1, 8, 3, 6, 2, 5, 9, 3, 0, 2 }; int maxVal = MaxValue(myArray); Console.WriteLine("The maximum value in myArray is {0}", maxVal); Console.ReadKey(); } }
示例的说明
这段代码包含一个函数,它执行的任务就是本章引言中示例函数所完成的任务。该函数以一个整数数组作为参数,并返回该数组中的最大值。该函数的定义如下所示:
static int MaxValue(int[] intArray) { int maxVal = intArray[0]; for (int i = 1; i < intArray.Length; i++) { if (intArray[i] > maxVal) maxVal = intArray[i]; } return maxVal; }
函数
MaxValue()
定义了一个参数,即int
数组intArray
,它还有一个int
类型的返回值。最大值的计算是很简单的。局部整型变量maxVal
初始化为数组中的第一个值,然后把这个值与数组中后面的每个元素依次进行比较。如果一个元素的值比maxVal
大,就用这个值代替当前的maxVal
值。循环结束时,maxVal
就包含数组中的最大值,用return
语句返回。
Main()
中的代码声明并初始化一个简单的整数数组,用于MaxValue()
函数:
int[] myArray = { 1, 8, 3, 6, 2, 5, 9, 3, 0, 2 };
调用
MaxValue()
,把一个值赋给int
变量maxVal
:
int maxVal = MaxValue(myArray);
接着,使用
Console.WriteLine()
把这个值写到屏幕上:
Console.WriteLine("The maximum value in myArray is {0}", maxVal);
1. 参数匹配
在调用函数时,必须使提供的参数与函数定义中指定的参数完全匹配,这意味着要匹配参数的类型、个数和顺序。例如,下面的函数:
static void MyFunction(string myString, double myDouble)
{
...
}
不能使用下面的代码调用:
MyFunction(2.6, "Hello"); ❌
这里试图把一个 double
值作为第一个参数传递,把 string
值作为第二个参数传递,参数顺序与函数声明中定义的顺序不匹配。
也不能使用下面的代码:
MyFunction("Hello"); ❌
这里仅传送一个
string
参数,而该函数需要两个参数。使用上述两个函数调用都会产生编译错误,因为编译器要求必须匹配所有函数的签名。使用函数的名称和参数来定义函数的签名。
再回顾这个示例,MaxValue()
只能用于获取整数数组中的最大 int
值。如果用下面的代码替换 Main()
中的代码:
static void Main(string[] args)
{
double[] myArray = { 1.3, 8.9, 3.3, 6.5, 2.7, 5.3 };
double maxVal = MaxValue(myArray); ❌
Console.WriteLine("The maximum value in myArray is {0}", maxVal);
Console.ReadKey();
}
就不能编译这段代码,因为参数类型是错误的。在本章后面的 “重载函数” 一节将介绍解决这个问题的一个有效技术。
2. 参数数组
C#允许为函数指定一个(只能指定一个)特殊参数,这个参数必须是函数定义中的最后一个参数,称为参数数组。参数数组允许用个数不定的参数调用函数,可使用 params
关键字定义它们。
参数数组可以简化代码,因为在调用代码中不必传递数组,而是传递同类型的几个参数,这些参数会被放在可在函数中使用的一个数组中。
定义使用参数数组的函数时,需要使用下列代码:
static <returnType> <FunctionName>(<p1Type> <p1Name>, ..., params <type>[] <name>)
{
...
return <returnValue>;
}
使用下面的代码可以调用该函数。
<FunctionName>(<p1>, ..., <val1>, <val2>, ...)
其中 <val1>
和 <val2>
等都是 <type>
类型的值,用于初始化 <name>
数组。可以指定的参数个数几乎不受限制,但它们都必须是 <type>
类型。甚至根本不必指定参数。
这一点使参数数组特别适合于函数提供一些额外的信息,供它们在处理过程中使用。例如,假定有一个函数 GetWord()
,它的第一个参数是一个 string
值,并返回字符串中的第一个单词。
string firstWord = GetWord("This is a sentence.");
其中 firstWord
被赋予字符串 This
。
可在 GetWord()
中添加一个 params
参数,以根据其索引选择另一个要返回的单词:
string firstWord = GetWord("This is a sentence.", 2);
假定第一个单词计数为 1,则 firstWord
就被赋予字符串 is
。
也可以在第 3 个参数中限制返回的字符个数,同样通过 params
参数来实现:
string firstWord = GetWord("This is a sentence.", 4, 3);
此时 firstWord
被赋予字符串 sen
。
下面的示例定义并使用带有
params
类型参数的函数。把下述代码添加到
Program.cs
中:
class Program { static int SumVals(params int[] vals) { int sum = 0; foreach (int val in vals) { sum += val; } return sum; } static void Main(string[] args) { int sum = SumVals(1, 5, 2, 9, 8); Console.WriteLine("Summed Values = {0}", sum); Console.ReadKey(); } }
示例的说明
这个示例用关键字
params
定义函数sumVals()
,该函数可以接受任意个int
参数(但不接受其他类型的参数):
static int SumVals(params int[] vals) { ... }
这个函数对
vals
数组中的值进行迭代,将这些值加在一起,返回其结果。在
Main()
中,用 5 个整型参数调用函数SumVals()
:
int sum = SumVals(1, 5, 2, 9, 8);
也可以用 0、1、2 或 100 个整型参数调用这个函数--参数的数量不受限制。
C# 4 中引入了指定函数参数的新方式,包括用一种可读性更好的方式来包含可选参数。第 14 章将介绍这些方法,从该章中可以了解到自问世以来,C#语言发生了怎样的变迁。
3. 引用参数和值参数
本章迄今定义的所有函数都带有值参数。其含义是,在使用参数时,是把一个值传递给函数使用的一个变量。对函数中此变量的任何修改都不影响函数调用中指定的参数。例如,下面的函数使传递过来的参数值加倍,并显示出来:
static void ShowDouble(int val)
{
val *= 2;
Console.WriteLine("val doubled = {0}", val);
}
参数 val
在这个函数中被加倍,如果按一下方式调用它:
int myNumber = 5;
Console.WriteLine("myNumber = {0}", myNumber);
ShowDouble(myNumber);
Console.WriteLine("myNumber = {0}", myNumber);
输出到控制台上的文本如下所示:
myNumber = 5
val doubled = 10
myNumber = 5
把 myNumber
作为一个参数,调用 ShowDouble()
并不影响 Main()
中 myNumber
大的值,即使把 myNumber
赋值给 val
后将 val
加倍,myNumber
的值也不变。
这很不错,但如果要改变 myNumber
的值,就会有问题。可以使用一个为 myNumber
返回新值的函数:
static int DoubleNum(int val)
{
val *= 2;
return val;
}
并使用下面的代码调用它:
int myNumber = 5;
Console.WriteLine("myNumber = {0}", myNumber);
myNumber = DoubleNum(myNumber);
Console.WriteLine("myNumber = {0}", myNumber);
但这段代码一点也不直观,且不能改变用作参数的多个变量值(因为函数只有一个返回值)。
此时可以通过 “引用”传递参数。即函数处理的变量与函数调用中使用的变量相同,而不仅仅是值相同的变量。因此,对这个变量进行的任何改变都会影响用作参数的变量值。为此,只需使用 ref
关键字指定参数:
static void ShowDouble(ref int val)
{
val *= 2;
Console.WriteLine("val doubled = {0}", val);
}
在函数调用中再次指定它(这是必须的,因为 ref
参数是函数签名的一部分):
int myNumber = 5;
Console.WriteLine("myNumber = {0}", myNumber);
ShowDouble(ref myNumber);
Console.WriteLine("myNumber = {0}", myNumber);
输出到控制台的文本如下所示:
myNumber = 5
val doubled = 10
myNumber = 10
这次,myNumber
被 ShowDouble()
修改了。
用作 ref
参数的变量有两个限制。首先,函数可能会改变引用参数的值,所以必须在函数调用中使用 “非常量” 变量。所以,下面的代码是非法的:
const int myNumber = 5;
Console.WriteLine("myNumber = {0}", myNumber);
ShowDouble(ref myNumber);
Console.WriteLine("myNumber = {0}", myNumber);
其次,必须使用初始化过的变量。C#不允许假定 ref
参数在使用它的函数中初始化,下面的代码也是非法的:
int myNumber; ShowDouble(ref myNumber); ❌
Console.WriteLine("myNumber = {0}", myNumber);
4. 输出参数
除了按引用传递值外,还可以使用 out
关键字,指定所给的参数是一个输出参数。 out
关键字的使用方式与 ref
关键字相同(在函数定义和函数调用中用作参数的修饰符)。实际上,它的执行方式与引用参数完全一样,因为在函数执行完毕后,该参数的值将返回给函数调用中使用的变量。但是,二者存在一些重要区别:
- 把未赋值的变量用作
ref
参数是非法的,但可以把未赋值的变量用作out
参数。* 另外,在函数使用out
参数时,必须把它看成是尚未赋值。
即调用代码可以把已赋值的变量用作 out
参数,但存储在该变量中的值会在函数执行时丢失。
例如,考虑前面返回数组中最大值的 MaxValue()
函数,略微修改该函数,获取数组中最大值的元素索引。为简单起见,如果数组中有多个元素的值都是这个最大值,只提取第一个最大值的索引。为此,修改函数,添加一个 out
参数,如下所示:
static int MaxValue(int[] intArray, out int maxIndex)
{
int maxVal = intArray[0];
maxIndex = 0;
for (int i = 1; i < intArray.Length; i++)
{
if (intArray[i] > maxVal)
{
maxVal = intArray[i];
maxIndex = i;
}
}
return maxVal;
}
可以采用以下方式使用该函数:
int[] myArray = { 1, 8, 3, 6, 2, 5 9, 3, 0, 2 };
int maxIndex;
Console.WriteLine("The maximum value in myArray is {0}", MaxValue(myArray, out maxIndex));
Console.WriteLine("The first occurrence of this value is at element {0}", maxIndex + 1);
结果如下:
The maximum value in myArray is 9
The first occurrence of this value is at element 7
注意 ⚠️,必须在函数调用中使用 out
关键字,就像 ref
关键字一样。
在屏幕上显示结果时,给返回的
maxIndex
的值加上 1。这样可以使索引更容易读懂,因此数组的第一个元素记为元素 1,而不是元素 0。
🔚